Repository navigation
Read the timeout-cleanup cause on cancellation contexts - #311
Conversation
CancellationContext.from_dict() reads the optional top-level `cause` (absent/"cancellation" ordinary, "timeout" for timeout cleanup, anything else refused) and, with the timeout cause only, `timeout_kind` and `work_deadline_at`. to_dict() writes them back exactly and leaves ordinary contexts byte-identical. A timeout request is raised at the authored call as WorkflowTimeoutCleanupRequested, a WorkflowCancelled subclass, so existing cancellation handlers run on timeout too. It stays BaseException-rooted and is not asyncio.CancelledError. Refs durable-workflow/workflow#863, durable-workflow/workflow#1129. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core settled the timeout-cleanup context shape (workflow#1129 at 14a4b944): the context carries only the optional top-level `cause` plus its existing cleanup_deadline_at. timeout_kind and work_deadline_at stay on WorkflowTimeoutCleanupStarted, so the context no longer reads or writes them. Port Core's scoped-descendant vector: a scoped child of a timeout root has no `cause` and is an ordinary cancellation, while its scope origin keeps the root's cause. Refs durable-workflow/workflow#863, durable-workflow/workflow#1129. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Owner: sdk-python, handed over by General. Independently reviewed optional cause parsing, exact round-trip, descendant cause isolation and both exception-delivery paths. Existing WorkflowCancelled handlers retain behavior; no timeout-cleanup capability is advertised. Next: Security review at 6c75557 and coordination with workflow#1129 before merging for a later release. This is excluded from 2.17.0. |
|
Security PASS at I checked the parser, exception selection, replay delivery and descendant behavior against the merged workflow#1122 contract and current Core cause vectors:
The exact head has the full CI matrix green. It is safe to ship as experimental parser/exception support; workflow#1129 remains separately blocked on its requested engine fixes. |
|
Security PASS is recorded for 6c75557. This parser/exception support remains preparatory and advertises no timeout capability; native follow-through is server#1201. Owner: sdk-python. Next: refresh after the bug-fix release, rerun required checks and obtain exact-head confirmation, then ship in a minor release. No wire interruption marker is added by this PR. |
|
Refreshed preparatory source head: 280c5ff, on the accepted 2.17.1 base. Its original parser/exception code is unchanged; only the main merge and placement of its changelog entry under Unreleased are new. Local 161 cancellation/corpus checks, Ruff, mypy and corpus policy pass. Owner: sdk-python. Next: fresh CI and exact-head Security countercheck. Capability advertisement and the wire interruption marker remain off; qualified feature publication/mapping follows Server#1201 and Workflow#1129. |
|
|
All 21 checks now pass at 280c5ff. General confirmed the disposition: merge the reviewed preparatory parser/exception support with experimental status and no timeout-cleanup capability advertisement; feature publication and worker-mapping acceptance remain owned by sdk-python under Server#1201/Workflow#1129. No standalone minor is needed just to align versions. Exact-head Security countercheck is requested before merge. |
|
Current next action/owner: Security confirms exact refreshed head 280c5ff (all 21 checks green), then sdk-python merges it without further source edits. I completed the main refresh, focused regression checks and full CI, and requested that countercheck directly. After merge, sdk-python retains worker mapping and qualified feature-publication ownership under Server#1201/Workflow#1129; General explicitly confirmed no standalone preparatory minor is needed. |
|
Security exact-head countercheck: PASS for I independently fetched the head and compared the previously accepted branch range ( Disposition remains deliberately narrow: this is preparatory cancellation-context parsing and the typed exception only. It advertises no timeout capability and is not evidence of worker/runtime timeout delivery. Merge only this exact head; qualified publication and worker mapping remain gated by Server #1201 and Workflow #1129. |
|
Merged the exact Security-approved 280c5ff head as ee2ad9d, with all 21 checks green. This is preparatory source only; published 2.17.1 and the stable portal remain the previously verified release. No timeout capability is advertised. As General directed, sdk-python retains worker mapping and qualified feature publication under Server#1201/Workflow#1129, to resume when the producer is qualified. |



SDK side of Workflow's deadline-triggered timeout cleanup (durable-workflow/workflow#863). It follows the contract in
docs/architecture/timeout-cleanup.md, the shape decided on durable-workflow/workflow#1122, and Core's draft durable-workflow/workflow#1129, now at 14a4b944.Context parsing
CancellationContext.from_dict()reads the optional top-levelcauseon v1 and v2 contexts:nullor"cancellation"means an ordinary cancellation;"timeout"means a timeout-cleanup request;ValueError.cleanup_deadline_atis the cleanup deadline, and the timeout kind and work deadline stay onWorkflowTimeoutCleanupStarted. Like any other unknown key,timeout_kindandwork_deadline_atare neither read nor written back.causeandis_timeoutare readable on the context.causeis never inherited. A child cancelled under a timeout request, scoped or not, has nocauseand is an ordinary cancellation. A scoped child'sscope_originstill keeps the root's cause, as Core's does.to_dict()writescauseback exactly as read, including an explicit"cancellation", which Core now keeps too. An ordinary context stays byte-identical.Exception
throw_if_cancellation_requested()) as the new experimentalWorkflowTimeoutCleanupRequested.WorkflowCancelled, the exception this SDK raises there for a cooperative cancellation request. Existingexcept WorkflowCancelled:handlers, saga compensation included, therefore run on timeout too.BaseException-rooted and is not anasyncio.CancelledError.causeis the cross-SDK discriminator for telling the two apart.Tests
CancellationContextCauseTestvectors at 14a4b944 are ported: ordinary, explicitcancellationround-trip, timeout, unknown cause refused, a descendant without the cause, a scoped descendant of a timeout root, and round-trip.except WorkflowCancelledhandler catching the timeout. An uncaught timeout request reportsWorkflowTimeoutCleanupRequestedas its type.cooperative-timeout-cleanup-cause.Local checks
These ran in Docker:
twine checkand the package smoke test;Not in this PR
timeout_cleanupworker capability. That waits for Server/native support.WorkflowTimeoutCleanupRequestedfailure to the cleanup outcomecompleted.🤖 Generated with Claude Code